# -*- coding: utf-8 -*-

from docutils import nodes
from sphinx.util.compat import Directive

import os
import os.path

import re

def embdoc():
    pass
    

class embdoc(nodes.Element): pass





def dfn_stat_machine( s, nextlines ):
    
    typedef = False
    struct = False
    mcommnet = False
    stack = 0
    
    i = 0
    j = 0
    
    while(True):
        
        if len(s) < 100 : 
            s = s + '\n' + ' '.join(nextlines[i].strip().split())
            i += 1
            continue
            
        if mcommnet :
            x = s.split('*/',1)
            if len(x) == 0 :
                s = s + nexlines[i]
                i += 1
                continue
            s = x[1]
            mcomment = False
        
        if s.startswith('/*') :
            s = s[2:]
            mcomment = True
            continue
        
        if struct == False and s.startswith('{') :
            return dfn
        
        if typedef == False and startswith('typedef') :
            typedef = True
            
    
    return



def parse_doc( d ):
    
    lns = d.splitlines()
    
    i = 0
    while( i < len(lns) ):
        
        
        # finding 
        
        m = re.match(r'\s*\/\*\s+embdoc\b',lns[i])
        
        if m is None :
            i += 1
            continue
        
        endm = lns[i][m.end():]
        
        if '*/' not in endm :
            
            i += 1
            si = i
            
            while( '*/' not in lns[i] and i < len(lns) ):
                i += 1
            
            if i >= len(lns) :
                raise Exception, 'error parse doc'
            
            doc, dfn = lns[i].split('*/',1)
            doc = lns[si:i] + [doc]
            lessindent = min( len(d) - len(d.replace('\t',' '*4).lstrip(' ')) 
                              for d in doc )
            #doc = [ ( si+li, d[lessindent:] ) for li, d in enumerate(doc) ]
            doc = [ d[lessindent:] for d in doc ]
        else :
            
            doc = []
            dfn = lns[i].split('*/',1)[1]
            
            si = i
        
        dfn = dfn.strip()
        
        while( dfn == '' ):
            i += 1
            dfn += lns[i].strip()
        
        j = i
        
        if dfn.startswith( '#' ):
            while( dfn.endswith('\\') ):
                dfn += '\n'+lns[i]
            dfn = dfn.splitlines()
            doc = ['::',''] + [ ' '*4 +d for d in dfn ] + [''] + doc
            yield si-len(dfn)-3, doc, j, '.. c:macro :: '+dfn
            continue
        
        typedef  = False
        if dfn.startswith( 'typedef' ) :
            typedef = True
            dfn = dfn[len('typedef'):].strip()
            while( dfn == '' ):
                i += 1
                dfn += lns[i].strip()
            
        if dfn.startswith('struct') :
            
            while( not ln[i].startswith('}') ):
                i += 1
                dfn += '\n'+lns[i]
            dfn += '\n'+lns[i].split(';')[0]+';'
            
            if typedef == True :
                name = dfn[-1].strip('};').strip()
                dfn = 'typedef ' + dfn
            else :
                name = dfn[0][len('struct'):].split()[0]
            
            dfn = dfn.splitlines()
            doc = ['::',''] + [ ' '*4 +d for d in dfn ] + [''] + doc
            
            yield si-len(dfn)-3, doc, j, '.. c:type :: '+name
            continue
        
        if typedef :
            
            dfn = ' '.join(dfn.strip().split())
            while( ';' not in dfn ):
                i += 1
                dfn += ' ' + ' '.join(lns[i].strip().split())
            dfn = dfn.split(';')[0]
            name = dfn.split()[-1]
            dfn = dfn + ';'
            dfn = dfn.splitlines()
            doc = ['::',''] + [ ' '*4 +d for d in dfn ] + [''] + doc
            yield si-len(dfn)-3, doc, j , '.. c:type :: '+name
            continue
        
        dfn = ' '.join(dfn.strip().split())
            
        while( '{' in doc ):
            i += 1
            dfn += ' ' + ' '.join(lns[i].strip().split())
        
        dfn = dfn.split('{')[0]
        
        yield si, doc, j, '.. c:function :: '+dfn
    
    return

def makeup_doc( fn, lno, doc, dfn ):
    
    print fn, lno, doc, dfn
    #r = [(0,dfn),] + [ (i,' '*4 + d) for i, d in doc ] + [(0,'')]
    r = [dfn,' '*4] + [ ' '*4+d for d in doc ] + ['']
    return (r,[ ( fn, lno+i-2 ) for i in range(len(r))])

def makeup_pkg( pkgname, docs ):
    
    data, items = zip(*docs)
    
    data = [ ' '*4+d for d in sum(data,[]) ]
    items = sum(items,[])
    
    data = [ '.. package :: ' + pkgname, ' '*4 ] + data
    items = [('',0),('',1)] + items
    
    return data, items

class EmbDoc(Directive):

    _default_flags = set(['path'])

    has_content = False
    required_arguments = 1
    optional_arguments = 1
    final_argument_whitespace = True
    option_spec = { 'filetype': (lambda x:x.split(',')),
                    'package': lambda x:x,
                  }

    def run(self):
        #print 'options:', self.options
        print 'content:', self.content, self.content.__class__
        #print 'arg:', self.arguments
        
        self.content.data = []
        self.content.items = []

        path = os.path.normpath( self.arguments[0] )
        
        pkgdoc = []
        
        for root, dirs, files in os.walk(path):
            for f in files :
                if not any( f.endswith(i) for i in ( '.c','.h' ) ):
                    continue
                fn = os.path.join(root, f)
                with open(fn, 'r') as fp :
                    c = fp.read()
                    dfns = [ makeup_doc(fn, lno, doc, dfn) 
                             for lno, doc, j, dfn in parse_doc(c)]
                    pkgdoc.extend(dfns)
        
        if 'package' in self.options :
            data, items = makeup_pkg( self.options['package'], pkgdoc )
        
        print data
        print items
        
        self.content.data = data
        self.content.items = items
        
        emb = embdoc()
        
        self.state.nested_parse(self.content, self.content_offset,
                                emb, match_titles=1)
        #self.state.nested_parse(pkgdoc, self.content_offset, emb, match_titles=1)
        
        return emb.children



def setup(app):
    app.add_node(embdoc)
    app.add_directive('embdoc', EmbDoc)
